package cn.psvmc.cxzapi.service.system;

import cn.psvmc.cxzapi.config.ConfigKey;
import cn.psvmc.cxzapi.config.mybatisplus.service.impl.AbstractService;
import cn.psvmc.cxzapi.constant.GlobalConstant;
import cn.psvmc.cxzapi.enums.ResultCode;
import cn.psvmc.cxzapi.exception.ServiceException;
import cn.psvmc.cxzapi.mapper.system.TMenuMapper;
import cn.psvmc.cxzapi.mapper.system.TUserMapper;
import cn.psvmc.cxzapi.mapper.system.TUserRoleMapper;
import cn.psvmc.cxzapi.model.common.SearchEntity;
import cn.psvmc.cxzapi.model.system.TUser;
import cn.psvmc.cxzapi.model.system.TUserRole;
import cn.psvmc.cxzapi.response.Result;
import cn.psvmc.cxzapi.response.ResultGenerator;
import cn.psvmc.cxzapi.util.JedisUtil;
import cn.psvmc.cxzapi.util.RecognizeIdcardUtil;
import cn.psvmc.cxzapi.util.SmsUtil;
import cn.psvmc.cxzapi.util.ToolUtils;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sun.istack.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户表 服务实现类
 *
 * @Author auto
 * @Date 2024-08-08
 */
@Service
public class TUserService extends AbstractService<TUserMapper, TUser> {

    @Resource
    private TUserMapper tUserMapper;
    @Resource
    private TUserRoleMapper userRoleMapper;
    @Resource
    private JedisUtil jedisUtil;
    @Resource
    private ServiceUtil serviceUtil;
    @Resource
    private TMenuMapper tMenuMapper;
    public static final int LOCK_TIME = 5 * 60; // 5 分钟
    public static final int MAX_FAIL_TIMES = 3;

    /**
     * @Date auto 2024-08-08 新增用户表
     * @Param tUser 用户表
     * @Return int
     */
    @Transactional(rollbackFor = Exception.class)
    public void add(TUser tUser) {
        if (ToolUtils.isEmpty(tUser.getUsername())) {
            tUser.setUsername(tUser.getUserphone());
        }
        Map map;
        if (tUser.getUsertype().equals(1)) {//平台用户
            map = tUserMapper.getUserByUserPhone(tUser.getUserphone());
            tUser.setStoreid(null);
        } else {
            map = tUserMapper.getUserByStoreIdAndPhone(tUser.getStoreid(), tUser.getUserphone());
        }

        if (map != null) {
            if (map.get("deleted").equals("0")) {
                throw new ServiceException("手机号已存在！");
            } else {//已删除用户添加更新数据
                tUser.setUserid(ToolUtils.objToInteger(map.get("userid")));
                userRoleMapper.deleteByUserId(tUser.getUserid());//删除用户角色
                tUser.setDeleted(0);
                tUserMapper.updateByUserId(tUser);
            }
        } else {
            String loginpwd = tUser.getLoginpwd();
            if (ToolUtils.isEmpty(loginpwd)) {
                loginpwd = "123456";
            }
            tUser.setLoginpwd(ToolUtils.getMD5Pwd(loginpwd));
            tUserMapper.insert(tUser);
        }

        saveUserRole(tUser);//保存用户角色
    }

    //保存用户角色
    private void saveUserRole(TUser tUser) {
        for (Integer roleId : tUser.getRoleIds()) {
            TUserRole ur = new TUserRole();
            ur.setRoleid(roleId);
            ur.setUserid(tUser.getUserid());
            userRoleMapper.insert(ur);
        }
    }

    /**
     * @Date auto 2024-08-08 更新用户表
     * @Param tUser 用户表模型
     * @Return int
     */
    @Transactional(rollbackFor = Exception.class)
    public int edit(TUser tUser) {
        // 通过id查询数据处理 详情
        TUser originTUser = this.detail(tUser.getUserid());
        if (originTUser == null) {
            throw new ServiceException("未查询到对应的用户表信息！");
        }

        // 比较数据版本号
        if (!originTUser.getRversion().equals(tUser.getRversion())) {
            throw new ServiceException("数据已更新，请刷新后重试！");
        }

        String loginpwd = tUser.getLoginpwd();
        if (ToolUtils.isNotEmpty(loginpwd) && !loginpwd.equals(originTUser.getLoginpwd())) {
            tUser.setLoginpwd(ToolUtils.getMD5Pwd(loginpwd));
        } else if (ToolUtils.isEmpty(loginpwd)) {
            tUser.setLoginpwd(null);
        }

        List<Integer> roleIds = tUser.getRoleIds();
        if (ToolUtils.isListNotEmpty(roleIds)) {
            userRoleMapper.deleteByUserId(tUser.getUserid());//删除用户角色
            saveUserRole(tUser);//保存用户角色
        }

        // 字段为null 不更新
        String userphone = tUser.getUserphone();
        if ("".equals(userphone)) {
            userphone = null;
        }
        tUser.setUserphone(userphone);
        tUser.setUsertype(null);
        tUser.setStoreid(null);
        return tUserMapper.updateById(tUser);
    }

    /**
     * @Date auto 2024-08-08 查询用户表详情
     * @Param id 唯一id
     * @Return cn.psvmc.cxzapi.model.system.TUser
     */
    public TUser detail(Integer id) {
        return tUserMapper.selectById(id);
    }

    /**
     * @Date auto 2024-08-08 删除用户表
     * @Param id 唯一id
     * @Param rversion 版本号
     * @Return int
     */
    public int delete(Integer id, Integer rversion) {
        // 查询用户表详情
        TUser tUser = this.detail(id);
        if (tUser == null) {
            throw new ServiceException("未查询到对应的用户表信息！");
        }

        // 比较数据版本号
        if (!rversion.equals(tUser.getRversion())) {
            throw new ServiceException("数据已更新，请刷新后重试！");
        }

        return tUserMapper.deleteById(id);
    }

    /**
     * @Date auto 2024-08-08 查询用户表列表
     * @Param iPage 分页
     * @Return com.baomidou.mybatisplus.core.metadata.IPage<cn.psvmc.cxzapi.model.system.TUser>
     */
    public IPage<Map> list(Page<TUser> iPage, SearchEntity entity) {
        serviceUtil.setStoreId(entity);
        return tUserMapper.listAll(iPage, entity);
    }

    /**
     * @Description 商家用户列表
     * @Date 2024/8/20 11:25
     * @Author YWX
     * @Param [iPage, entity]
     * @Return com.baomidou.mybatisplus.core.metadata.IPage<java.util.Map>
     **/
    public IPage<Map> listSj(Page<TUser> iPage, SearchEntity entity) {
        return tUserMapper.listSj(iPage, entity);
    }

    /**
     * 登录
     *
     * @return cn.psvmc.cxzapi.response.Result
     * @Param [tUser, request]
     * @Author ywx
     * @Date 2024/8/8 22:14
     **/
    public Result loginByUserPhone(TUser tUser) {
        String userphone = tUser.getUserphone();
        String msgcode = tUser.getMsgcode();
        List<Map<String, Object>> stores = tUserMapper.listStoreByPhone(userphone);//获取用户的商家列表
        if (ToolUtils.isListEmpty(stores)) {
            return ResultGenerator.genFailResult(ResultCode.AUTH_ACCOUNT_NOTEXISTS);
        } else if (stores.get(0).get("userlock").equals(1)) {
            return ResultGenerator.genFailResult(ResultCode.ACCOUNT_LOCKED);
        }

        // 检查账号是否被锁定
        String lockKey = "lock:" + userphone;
        String failCountKey = "fail_count:" + userphone;
        String lockNum = jedisUtil.get(lockKey);
        int failCount = 0;
        String failNum = jedisUtil.get(failCountKey);
        if (ToolUtils.isNotEmpty(failNum)) {
            failCount = ToolUtils.objToInteger(failNum);
        }
        if (failCount >= 3) {
            // 获取剩余锁定时间
            Long remainingTime = jedisUtil.getExpire(failCountKey);
            return ResultGenerator.genFailResult("登录验证码错误次数过多，请" + ToolUtils.formatSeconds(remainingTime) + "后重试");
        }

        String rediscode = jedisUtil.get(userphone);
        //String rediscode = "1234";
        if (ToolUtils.isEmpty(rediscode)) {
            ResultCode code = ResultCode.MSG_NOTEXISTS;
            return ResultGenerator.genFailResult(code);
        }

        if (!msgcode.equals(rediscode)) {
            String num = jedisUtil.get(failCountKey);
            // 验证码校验失败，增加失败次数
            failCount++;
            int lockCount = 0;//锁定次数
            if (ToolUtils.isNotEmpty(lockNum)) {
                lockCount = ToolUtils.objToInteger(lockNum);
            }
            if (failCount >= MAX_FAIL_TIMES) {
                lockCount++;
                // 失败次数达到 3 次，锁定账号 5 分钟
                jedisUtil.set(failCountKey, failCount, LOCK_TIME);
                jedisUtil.set(lockKey, lockCount);
                if (lockCount >= 3) {// 锁定次数达到 3 次，账号被锁定
                    tUserMapper.updateUserLockByPhone(userphone, 1);
                    return ResultGenerator.genFailResult(ResultCode.ACCOUNT_LOCKED);
                }
                return ResultGenerator.genFailResult("登录验证码错误次数过多，请5分钟后重试");
            } else {
                jedisUtil.set(failCountKey, failCount);
            }
            ResultCode code = ResultCode.MSG_ERROR;
            return ResultGenerator.genFailResult(code);
        }

        //redis
        String token = ToolUtils.getToken();//获取token
        boolean redis_rtn = jedisUtil.set(userphone, token, ConfigKey.redisdatatime);
        if (!redis_rtn) {
            ResultCode code = ResultCode.TOKEN_ADD_ERROR;
            return ResultGenerator.genFailResult(code);
        } else {
            for (Map<String, Object> store : stores) {
                store.put("Cxz-Token-Key", userphone);
                store.put("Cxz-Token-Value", token);
            }
            // 验证码校验成功，清除失败次数计数器
            jedisUtil.delete(failCountKey);
            jedisUtil.delete(lockKey);
            return ResultGenerator.genSuccessResult("登录成功！", stores);
        }
    }

    /**
     * @Description 选择商家
     * @Date 2024/8/15 9:48
     * @Author YWX
     * @Param [userid]
     * @Return cn.psvmc.cxzapi.response.Result
     **/
    public Result selectStore(Integer userid) {
        Map<String, Object> user = tUserMapper.getUserById(userid);
        return checkUser(user);//校验用户信息
    }

    //校验用户信息
    private Result checkUser(Map<String, Object> user) {
        Result result;
        if (ToolUtils.isEmpty(user)) {
            ResultCode code = ResultCode.AUTH_ACCOUNT_NOTEXISTS;
            result = ResultGenerator.genFailResult(code);
        } else {
            result = saveToken(user);//保存token信息
        }
        return result;
    }

    //保存token信息
    private Result saveToken(Map<String, Object> user) {
        //redis
        String token = ToolUtils.getToken();//获取token
        String key = ToolUtils.objToString(user.get("userphone"));//获取tokenKey
        boolean redis_rtn = jedisUtil.set(key, token, ConfigKey.redisdatatime);
        if (!redis_rtn) {
            ResultCode code = ResultCode.TOKEN_ADD_ERROR;
            return ResultGenerator.genFailResult(code);
        } else {
            user.put("Cxz-Token-Key", key);
            user.put("Cxz-Token-Value", token);
            return ResultGenerator.genSuccessResult("登录成功！", user);
        }
    }

    private String getTokenKey(Object userid) {
        return GlobalConstant.TAG_USER + userid;
    }

    /**
     * @Description 获取验证码
     * @Date 2024/8/10 22:02
     * @Author YWX
     * @Param [userphone]
     * @Return cn.psvmc.cxzapi.response.Result
     **/
    public Result getMsgCode(String userphone) {
        String failCountKey = "fail_count:" + userphone;
        int failCount = 0;
        String failNum = jedisUtil.get(failCountKey);
        if (ToolUtils.isNotEmpty(failNum)) {
            failCount = ToolUtils.objToInteger(failNum);
        }
        if (failCount >= 3) {
            // 获取剩余锁定时间
            Long remainingTime = jedisUtil.getExpire(failCountKey);
            return ResultGenerator.genFailResult("登录验证码错误次数过多，请" + ToolUtils.formatSeconds(remainingTime) + "后重试");
        }

        Map user = tUserMapper.countByTypeAndPhone(null, userphone);//获取指定类型和手机号下账户数量
        Integer num = ToolUtils.objToInteger(user.get("num"));
        if (num.compareTo(1) < 0) {
            return ResultGenerator.genFailResult(ResultCode.AUTH_ACCOUNT_NOTEXISTS);
        } else if (user.get("userlock").equals(1)) {
            return ResultGenerator.genFailResult(ResultCode.ACCOUNT_LOCKED);
        }
        try {
            //开发测试模式
            if (ConfigKey.enableTest) {
                Integer code = 1234;
                jedisUtil.set(userphone, code, 300);
            } else {
                Integer code = ToolUtils.messageCode();
                //阿里云短信
                Integer result = SmsUtil.sendSms(userphone, code);
                if (1 == result) {
                    // 运通短信
                    result = SmsUtil.sendSySms(userphone, code);
                }
                if (0 == result) {
                    jedisUtil.set(userphone, code, 300);//验证码有效时间为5分钟
                } else {
                    return ResultGenerator.genFailResult("获取验证码异常！");
                }
            }
        } catch (Exception e) {
            log.error("获取验证码失败：" + e.getMessage());
            return ResultGenerator.genSuccessResult("获取验证码失败：" + e.getMessage());
        }
        return ResultGenerator.genSuccessResult("获取验证码成功！");
    }

    /**
     * @Description 身份证识别
     * @Date 2024/8/10 22:56
     * @Author YWX
     * @Param [file]
     * @Return cn.psvmc.cxzapi.response.Result
     **/
    public Result getIdCard(MultipartFile file) {
        if (file == null) {
            return ResultGenerator.genFailResult("文件不能为空！");
        }
        try {
            String idCard = RecognizeIdcardUtil.recognizeIdCard(file);
            return ResultGenerator.genSuccessResult("身份证识别成功！", idCard);
        } catch (Exception e) {
            log.error("身份证识别失败：" + e.getMessage());
            return ResultGenerator.genFailResult("身份证识别失败：" + e.getMessage());
        }
    }

    /**
     * @Description 重置密码
     * @Date 2024/8/13 12:47
     * @Author YWX
     * @Param [user]
     * @Return cn.psvmc.cxzapi.response.Result
     **/
    public Result resetPwd(TUser user) {
        String loginpwd = user.getLoginpwd();
        if (ToolUtils.isEmpty(loginpwd)) {
            loginpwd = "123456";
        }
        user.setLoginpwd(ToolUtils.getMD5Pwd(loginpwd));
        tUserMapper.updateById(user);
        return ResultGenerator.genSuccessResult("重置成功！");
    }

    /**
     * @Description 权限菜单
     * @Date 2024/8/13 15:54
     * @Author YWX
     * @Param [userid]
     * @Return java.util.List<java.util.Map < java.lang.String, java.lang.Object>>
     **/
    public List<Map<String, Object>> listMenu() {
        TUser user = serviceUtil.getUser();
        Integer userid = user.getUserid();
        String menuIdOperates = tUserMapper.getMenuIdsByUserId(userid);
        if (ToolUtils.isEmpty(menuIdOperates)) return new ArrayList<>();
        String[] menuidOperates = menuIdOperates.split(";");
        List<String> menuIdList = new ArrayList<>();
        Map<String, String> menuOperateMap = new HashMap<>();
        for (String menuidOperate : menuidOperates) {
            String[] menuidOperateArr = menuidOperate.split("_");
            String menuid = menuidOperateArr[0];
            menuIdList.add(menuid);
            String operate = "";
            if (menuidOperateArr.length > 1) {
                operate = menuidOperateArr[1];
            }
            if (menuOperateMap.containsKey(menuid)) {
                operate = menuOperateMap.get(menuid) + "," + operate;
                operate = Arrays.stream(operate.split(",")).distinct().collect(Collectors.joining(","));
            }
            menuOperateMap.put(menuid, operate);
        }
        String menuIds = menuIdList.stream().collect(Collectors.joining(","));
        List<Map<String, Object>> list = tUserMapper.listMenu(menuIds, user.getUsertype());
        for (Map<String, Object> map : list) {
            map.put("operates", menuOperateMap.get(ToolUtils.objToString(map.get("menuid"))));
        }
        return buildMenuTree(list);
    }

    private static @NotNull List<Map<String, Object>> buildMenuTree(List<Map<String, Object>> list) {
        List<Map<String, Object>> list2 = new ArrayList<>();
        List<Map<String, Object>> builtTree = ToolUtils.buildTree(list, "menuid", "menupid", "childList");
        for (Map<String, Object> map : builtTree) {
            if (map.get("leaf").equals(1)) {
                list2.add(map);
                continue;
            }
            //判断是否有子菜单
            if (map.get("leaf").equals(0) && ToolUtils.isNotEmpty(map.get("childList"))) {
                list2.add(map);
            }
        }
        return list2;
    }

    /**
     * @Description 角色权限菜单
     * @Date 2025/1/22 22:48
     * @Author YWX
     * @Param []
     * @Return java.util.List<java.util.Map < java.lang.String, java.lang.Object>>
     **/
    public List<Map<String, Object>> listRoleMenu() {
        TUser user = serviceUtil.getUser();
        if (user == null) return new ArrayList<>();
        List<Map<String, Object>> list = tMenuMapper.listByUserType(user.getUsertype());
        return buildMenuTree(list);
    }

    /**
     * @Description 解锁
     * @Date 2025/2/21 22:14
     * @Author YWX
     * @Param [userphone]
     * @Return void
     **/
    public void unLock(String userphone) {
        tUserMapper.updateUserLockByPhone(userphone, 0);
        jedisUtil.delete("lock:" + userphone);
        jedisUtil.delete("fail_count:" + userphone);
    }
}
